home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
chatr2.zip
/
CHATTER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-21
|
38KB
|
1,086 lines
/**************************************************************
Chatter
Copyright (c) 1992 Kurt Duncan - All Rights Reserved
An interactive communications program based on the NOVELLtm
IPX/SPX transport mechanism.
Command line:
CHATTER [<ident>] [<options>]
Options:
-r<rows on display>
sets number of rows of display (25, 43, 50)
defaults to 25
-m
indicates a monochrome monitor
-s<hex_socket_number>
indicates the IPX socket number to be used
defaults to 5000h
-b<number_of_buffers>
indicates the number of ECB/IPX_header buffers
that will be allocated for receiving network traffic
defaults to 16
-n<hex-network-number>
a network number to be added to the list of broadcast
networks
**************************************************************/
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include "ipxlib.h"
#define PROCESSOR_NAME "CHATTER V1.20"
/*****************************************************************
The following codes are arbitrary values which we will use
to classify the various messages we are going to deal with.
The code is stored as the first data byte in each IPX message.
*****************************************************************/
#define JOIN_CODE 0x00
#define LEAVE_CODE 0x01
#define PROBE_CODE 0x02
#define RESPOND_CODE 0x03
#define MESSAGE_CODE 0x0F
/******************************************************************
Stuff for dealing with color attributes... and which
automatically choose monochrome attributes if Color_Display == 0
******************************************************************/
#define COLOR_TEST(cv, mv) (unsigned char) (Color_Display ? cv : mv)
#define WHITE_ON_BLACK 0x0F
#define GREY_ON_BLACK 0x07
#define BLACK_ON_WHITE 0x70
#define WHITE_ON_RED 0x4F
#define YELLOW_ON_BLACK 0x0E
#define GREEN_ON_BLACK 0x0A
#define CYAN_ON_BLACK 0x0B
#define CYAN_ON_BLUE 0x1B
#define YELLOW_ON_GREEN 0x2E
#define PROCESSOR_LINE_COLOR COLOR_TEST (WHITE_ON_RED, BLACK_ON_WHITE)
#define TEXT_COLOR COLOR_TEST (GREEN_ON_BLACK, GREY_ON_BLACK)
#define JOIN_COLOR COLOR_TEST (WHITE_ON_RED, BLACK_ON_WHITE)
#define IDENT_COLOR COLOR_TEST (CYAN_ON_BLACK, GREY_ON_BLACK)
#define MESSAGE_COLOR COLOR_TEST (YELLOW_ON_BLACK, WHITE_ON_BLACK)
#define INPUT_COLOR COLOR_TEST (CYAN_ON_BLUE, BLACK_ON_WHITE)
#define TRACE_COLOR COLOR_TEST (YELLOW_ON_GREEN, BLACK_ON_WHITE)
/****************************************
Macros which think they are functions
****************************************/
#define Screen_Write(p) Screen_Write_Attr (p, Screen_Attribute)
#define Screen_Append(p) Screen_Append_Attr (p, Screen_Attribute)
#define MK_FP(seg, off) (void far *) ((((unsigned long) seg) << 16) | (off))
/*********************************************************
Functions which are described further down in the code
*********************************************************/
void Show_Node_Address (unsigned char *Node, unsigned char *Msg);
void Clear_Buffer (void);
void Screen_Clear (void);
void Screen_ClearLast (void);
void Screen_Scroll (void);
void Screen_Write_Attr (char *Inst, char Attr);
int Initialization (int argc, char *argv[]);
void Termination (void);
void Setup_Send (char Code, char *Inmsg);
void Setup_Send_Specific (char Code, char *Inmsg, struct IPX_address *Addr);
void Execute_Command (char *CString);
void Poll (void);
void Poll_Key (void);
void Poll_Net (void);
/***********************************************************
Various Data Structures are defined and/or declared here
***********************************************************/
struct Character_Cell { /* A definition of a screen position in video */
char Character; /* memory. Such memory is assumed to begin at */
char Attribute; /* address B800:0000. */
};
struct IPX_ECB Send_ECB; /* An ECB packet for sending data */
struct IPX_header Send_IPXH; /* An IPX header packet for sending data */
unsigned int Buffers; /* The number of buffers we will support */
struct IPX_ECB *ECBPointer; /* Pointer to first ECB buffer */
struct IPX_header *IPXHPointer; /* Pointer to first IPX header buffer */
char Buffer[80]; /* Input buffer - holds user input */
unsigned int Bufsub; /* Current number of chars in buffer */
unsigned int Socket_Number; /* IPX socket number we communicate on */
unsigned int Color_Display; /* zero if monochrome, nonzero otherwise */
char Ident[9]; /* user's identifier, from command line */
struct Character_Cell far *Screen; /* Pointer to video memory */
char Screen_Attribute; /* Default attribute for erasing screen */
unsigned int Screen_Rows; /* Number of rows on screen */
unsigned int Append_Suboff; /* Last screen position written to */
unsigned int Trace; /* Trace flag; nonzero indicates trace on */
unsigned int All_Done; /* nonzero indicates user wants to quit */
#define NetNumCountMax 256
unsigned int NetNumCount; /* Stuff to support multiple networks */
unsigned long int NetworkNumber[NetNumCountMax];
/******************************************************************
The following routine takes the Node portion of an IPXH_address
structure, and converts the six byte field to an ASCII string
that can be displayed. An example of the output would look
similar to:
00.00.1B.2C.19.37
******************************************************************/
void Show_Node_Address (unsigned char *Node, unsigned char *Msg)
{
int inx, outx, lcv;
char ch, cl;
inx = 0;
outx = 0;
for (lcv = 0; lcv < 6; lcv++)
{
ch = (Node[inx] >> 4) | 0x30;
if (ch > '9') ch += 7;
cl = (Node[inx++] & 0x0F) | 0x30;
if (cl > '9') cl += 7;
Msg[outx++] = ch;
Msg[outx++] = cl;
if (lcv < 5) Msg[outx++] = '.';
}
Msg[outx] = 0x00;
return;
}
/****************************************************************
The following routine sets all of the bytes of the internally
maintained input buffer to binary zero.
****************************************************************/
void Clear_Buffer (void)
{
int sub;
Bufsub = 0;
for (sub = 0; sub < 80; sub++)
Buffer[sub] = 0;
return;
}
/*****************************************************************
Using direct memory access, we set the entire screen to blanks
(hex 0x20), with an attribute value as specified by
Screen_Attribute.
*****************************************************************/
void Screen_Clear (void)
{
int sub;
for (sub = 0; sub < (Screen_Rows * 80); sub++)
{
Screen[sub].Character = 0x20;
Screen[sub].Attribute = Screen_Attribute;
}
return;
}
/************************************************************
Sets the last line of the display to all blanks, with the
attribute specified by INPUT_COLOR.
************************************************************/
void Screen_ClearLast (void)
{
unsigned int sub, cnt;
sub = (Screen_Rows - 1) * 80;
for (cnt = 0; cnt < 80; cnt++)
{
Screen[sub].Character = 0x20;
Screen[sub++].Attribute = INPUT_COLOR;
}
return;
}
/************************************************************
Scrolls the display area of the screen (basically, all of
the screen except for the last row) up by one line. The
last line of the display area is set to blanks, with the
attribute specified by Screen_Attribute.
************************************************************/
void Screen_Scroll (void)
{
unsigned int sub, suboff;
suboff = 80;
for (sub = 0; sub < (Screen_Rows - 2) * 80; sub++)
{
Screen[sub].Character = Screen[suboff].Character;
Screen[sub].Attribute = Screen[suboff].Attribute;
suboff++;
}
suboff = (Screen_Rows - 2) * 80;
for (sub = 0; sub < 80; sub++)
{
Screen[suboff].Character = 0x20;
Screen[suboff++].Attribute = Screen_Attribute;
}
return;
}
/****************************************************************
Scrolls the display area, and writes the given string to the
last line of the display area, using the specified attribute.
We keep track of the last screen position written to, in case
the calling routine decides to call Screen_Append_Attr.
The Screen_Write macro calls this function, with the
Screen_Attribute value as the second parameter.
****************************************************************/
void Screen_Write_Attr (char *Inst, char Attr)
{
unsigned int sub, limit, suboff;
Screen_Scroll ();
limit = strlen (Inst) + 1;
if (limit > 80) limit = 80;
suboff = (Screen_Rows - 2) * 80;
for (sub = 0; sub < limit; sub++) {
Screen[suboff].Character = Inst[sub];
Screen[suboff++].Attribute = Attr;
}
Append_Suboff = suboff;
return;
}
/***************************************************************
Writes the specified output to the display area, starting at
the position which follows the last position written to by
either Screen_Write_Attr or Screen_Append_Attr, using the
attribute specified. We keep track of the last screen
position written to, in case the calling routine decides to
call us again.
The Screen_Append macro calls this function, with the
Screen_Attribute value as the second parameter.
***************************************************************/
void Screen_Append_Attr (char *Inst, char Attr) {
unsigned int sub, limit, suboff;
limit = strlen (Inst);
suboff = Append_Suboff;
for (sub = 0; sub < limit; sub++) {
Screen[suboff].Character = Inst[sub];
Screen[suboff++].Attribute = Attr;
}
Append_Suboff = suboff;
return;
}
/*****************************************************************
We do a lot of stuff here. See embedded comments for details.
This code happends once, when the program first executes. The
code has been placed here, so that the main function can stay
relatively clean. Basically, we do all setup, and return a
zero value (false) if something went wrong. Otherwise, we
return a nonzero value (true) to indicate to main that all is
okay.
*****************************************************************/
int Initialization (int argc, char *argv[])
{
union REGS regs;
unsigned int sub, scsub;
char tempst[80];
void far *fp;
/* Check whether IPX is loaded */
if (!(IPX_Is_Loaded ()))
{
printf ("IPX is not loaded!\n");
return (0);
}
/* Set default values */
/* Default socket is 5000 hex, */
/* Default display mode is color, */
/* trace is cleared, */
/* buffers is set to 16, */
/* base screen address is set to B800:0000, */
/* screen rows is set to 25, */
/* screen attribute is set to dim white on black */
/* Number of network number entries is set to 0 */
Socket_Number = 0x5000;
strcpy (Ident, "<anon> ");
Color_Display = 1;
Trace = 0;
Buffers = 16;
Screen = MK_FP (0xB800, 0x0000);
Screen_Rows = 25;
Screen_Attribute = 0x0007;
NetNumCount = 0;
/* Read command line parameters */
/* The first parameter without a leading hyphen is considered to */
/* be an ident code. The next such parameter causes an error. */
/* All parameters which start with a hyphen are considered */
/* switches, and are compared against the valid switches. */
for (sub = 1; sub < argc; sub++)
{
if (strlen (argv[sub]) > 20)
{
printf ("Parameter %u is too long\n", sub);
return (0);
}
strcpy (tempst, argv[sub]);
if (tempst[0] != '-')
{
if (strlen (tempst) > 8)
{
printf ("Ident code \"%s\" at parameter %u is too long - \n"
" limit is eight characters\n", tempst, sub);
return (0);
}
if (strcmp (Ident, "<anon> ") != 0)
{
printf ("Unrecognized text \"%s\" at parameter %u\n",
tempst, sub);
return (0);
}
else
{
strcpy (Ident, tempst);
while (strlen (Ident) < 8)
strcat (Ident, " ");
}
}
else
{
char t2[19];
int ti;
unsigned long int nn;
strcpy (t2, &tempst[2]);
switch (tempst[1])
{
case 'r':
case 'R':
ti = sscanf (t2, "%u", &Screen_Rows);
if ((ti == 0) || (Screen_Rows > 50))
{
printf ("Invalid number of rows given - "
"Option \"%s\" at parameter %u\n",
tempst, sub);
return (0);
}
break;
case 'm':
case 'M':
if (t2[0] != 0x00)
printf ("Extraneous text \"%s\" on -m option "
"at parameter %u ignored\n",
t2, sub);
Color_Display = 0;
break;
case 's':
case 'S':
ti = sscanf (t2, "%X", &Socket_Number);
if (ti == 0)
{
printf ("Invalid Socket Number given - "
"Option \"%s\" at parameter %u\n",
tempst, sub);
return (0);
}
break;
case 'b':
case 'B':
ti = sscanf (t2, "%u", &Buffers);
if (ti == 0)
{
printf ("Invalid Buffer count given - "
"Option \"%s\" at parameter %u\n",
tempst, sub);
return (0);
}
break;
case 'n':
case 'N':
ti = sscanf (t2, "%lX", &nn);
if (ti == 0)
{
printf ("Invalid Network Number given - "
"Option \"%s\" at parameter %u\n",
tempst, sub);
break;
}
if (NetNumCount == NetNumCountMax)
{
printf ("Internal Network Number table is full - "
"Ignoring Option \"%s\" at parameter %u\n",
tempst, sub);
break;
}
NetworkNumber[NetNumCount] = nn;
NetNumCount++;
break;
default:
printf ("Unrecognized option \"%s\" at parameter %u\n",
tempst, sub);
return (0);
}
}
}
/* Allocate buffer space for listen ECB's and IPX headers, */
/* then initialize the relevant ECB fields. Finally, set */
/* up all the buffers for listening. */
/* We use multiple buffers because it is certain that we */
/* will, sooner or later, encounter a condition where we */
/* cannot fully process an input message before another one */
/* is received. Thus, we chain several buffers to our */
/* socket, and let IPX use them up one by one. As we */
/* process them, we set them back up for listening. If we */
/* are lucky, we will never get more traffic than we can */
/* handle. */
(void *) ECBPointer =
(void *) malloc (Buffers * sizeof (struct IPX_ECB));
if (ECBPointer == NULL)
{
printf ("Unable to allocate memory for ECB buffers\n");
return (0);
}
(void *) IPXHPointer =
(void *) malloc (Buffers * sizeof (struct IPX_header));
if (IPXHPointer == NULL)
{
free (ECBPointer);
printf ("Unable to allocate memory for IPX header buffers\n");
return (0);
}
for (sub = 0; sub < Buffers; sub++)
{
ECBPointer[sub].In_Use = 01;
ECBPointer[sub].ESR_Address.segment = 0;
ECBPointer[sub].ESR_Address.offset = 0;
ECBPointer[sub].Socket_Number = IPX_Flipword (Socket_Number);
ECBPointer[sub].Fragment_Count = 1;
fp = &IPXHPointer[sub];
ECBPointer[sub].Fragment_Desc[0].Address.segment = FP_SEG (fp);
ECBPointer[sub].Fragment_Desc[0].Address.offset = FP_OFF (fp);
ECBPointer[sub].Fragment_Desc[0].Size = 576;
}
IPX_Open_Socket (Socket_Number);
for (sub = 0; sub < Buffers; sub++)
IPX_Listen_For_Packet (&ECBPointer[sub]);
/* Hide the DOS cursor */
regs.h.ah = 0x03;
int86 (0x10, ®s, ®s);
regs.h.ch |= 0x20;
regs.h.ah = 0x01;
int86 (0x10, ®s, ®s);
/* Clear the screen, and set the buffer cursor (a simulated cursor) */
Screen_Clear ();
Screen_ClearLast ();
Clear_Buffer ();
scsub = (Screen_Rows - 1) * 80;
Screen[scsub].Character = '_';
Screen[scsub].Attribute = INPUT_COLOR;
/* Display initial messages */
Screen_Write_Attr (PROCESSOR_NAME, PROCESSOR_LINE_COLOR);
sprintf (tempst, "Ident: %s", Ident);
Screen_Write_Attr (tempst, TEXT_COLOR);
sprintf (tempst, "Socket: %xh", Socket_Number);
Screen_Write_Attr (tempst, TEXT_COLOR);
sprintf (tempst, "Buffers: %u", Buffers);
Screen_Write_Attr (tempst, TEXT_COLOR);
Screen_Write_Attr ("Enter \\HELP for a list of commands",
TEXT_COLOR);
/* Send broadcast messages, type JOIN_CODE and PROBE_CODE */
Setup_Send (JOIN_CODE, "");
Setup_Send (PROBE_CODE, "");
return (1);
}
/*************************************************************
Termination is fairly simple:
Cancel all ECB packets. The IPXLIB routines will ignore
such requests for all packets that might not currently
be on the listen queue.
Free the memory previously occupied by the ECB and IPX
header buffers.
Send one final message, LEAVE_CODE, to indicate to the
other users on the network that you are going away.
Close the IPX socket.
Clear the screen.
Restore the DOS cursor.
*************************************************************/
void Termination (void)
{
union REGS regs;
int sub;
for (sub = 0; sub < Buffers; sub++)
IPX_Cancel_Event (&ECBPointer[sub]);
free (IPXHPointer);
free (ECBPointer);
Setup_Send (LEAVE_CODE, "");
IPX_Close_Socket (Socket_Number);
Screen_Clear ();
regs.h.ah = 0x03;
int86 (0x10, ®s, ®s);
regs.h.ch &= 0xDF;
regs.h.ah = 0x01;
int86 (0x10, ®s, ®s);
return;
}
/********************************************************************
The following routine sets up the ECB and IPX header packets for
sending the specified message. The caller provides a message
code (JOIN_CODE, LEAVE_CODE, etc), and a message. We send the
message as a broadcast to each network in the network number table.
If the table is empty, we send to network 0 (the local network).
********************************************************************/
void Setup_Send (char Code, char *Inmsg)
{
void far *fp;
unsigned int sub;
char tmsg[80], cmsg[20], nmsg[30];
struct IPX_address addr;
if (strlen (Inmsg) > 500) return;
memcpy (addr.Node.ch, "\xFF\xFF\xFF\xFF\xFF\xFF", 6);
addr.Socket = IPX_Flipword (Socket_Number);
if (NetNumCount == 0)
{
addr.Network = 0;
Setup_Send_Specific (Code, Inmsg, &addr);
}
else
for (sub = 0; sub < NetNumCount; sub++)
{
addr.Network = NetworkNumber[sub];
IPX_Fliplong (&addr.Network);
Setup_Send_Specific (Code, Inmsg, &addr);
}
return;
}
/********************************************************************
The following routine sets up the ECB and IPX header packets for
sending the specified message. The caller provides a message
code (JOIN_CODE, LEAVE_CODE, etc), a message, and an IPX address
structure which indicates the node to which the message is to be
sent. The first thing we do is set up the IPX header, which is
followed by the actual data. The data sent is formatted as such:
Byte +00: message code 1 byte
Byte +01: Ident, from command line 8 bytes
Byte +09: message zero to 500 bytes
The next thing we do is we call the IPX_Get_Local_Target function
to determine what the immediate address in the ECB should be (in
case we need to cross through a router), and then we set up the
ECB packet.
If Trace is set, we send a message to the local display.
Finally, we send the message itself, and wait until we are sure
the message actually got sent (not necessarily received, though).
********************************************************************/
void Setup_Send_Specific (char Code,
char *Inmsg,
struct IPX_address *Addr)
{
void far *fp;
char tmsg[80], cmsg[20], nmsg[30];
if (strlen (Inmsg) > 500) return;
Send_IPXH.Packet_Type = 0;
Send_IPXH.Destination.Network = Addr->Network;
memcpy (Send_IPXH.Destination.Node.ch, Addr->Node.ch, 6);
Send_IPXH.Destination.Socket = Addr->Socket;
Send_IPXH.Data[0] = Code;
memcpy (&Send_IPXH.Data[1], Ident, 8);
strcpy (&Send_IPXH.Data[9], Inmsg);
IPX_Get_Local_Target (&Send_IPXH.Destination,
&Send_ECB.Immediate_Address);
Send_ECB.ESR_Address.segment = 0;
Send_ECB.ESR_Address.offset = 0;
Send_ECB.Socket_Number = IPX_Flipword (Socket_Number);
Send_ECB.Fragment_Count = 1;
fp = &Send_IPXH;
Send_ECB.Fragment_Desc[0].Address.segment = FP_SEG (fp);
Send_ECB.Fragment_Desc[0].Address.offset = FP_OFF (fp);
Send_ECB.Fragment_Desc[0].Size = 30 + 8 + strlen (Inmsg) + 1;
if (Trace)
{
switch (Send_IPXH.Data[0])
{
case JOIN_CODE:
strcpy (cmsg, "JOIN ");
break;
case LEAVE_CODE:
strcpy (cmsg, "LEAVE ");
break;
case PROBE_CODE:
strcpy (cmsg, "PROBE ");
break;
case RESPOND_CODE:
strcpy (cmsg, "RESPOND");
break;
case MESSAGE_CODE:
strcpy (cmsg, "MESSAGE");
break;
default:
sprintf (cmsg, "UNKNOWN");
break;
}
Show_Node_Address (Send_IPXH.Destination.Node.ch, nmsg);
sprintf (tmsg, " - Sending %s packet to Network %lu Node ",
cmsg, Send_IPXH.Destination.Network);
strcat (tmsg, nmsg);
Screen_Write_Attr (tmsg, TRACE_COLOR);
}
IPX_Send_Packet (&Send_ECB);
while (Send_ECB.In_Use == 0xFF)
IPX_Relinquish_Control ();
return;
}
/**********************************************************************
This function executes the command which is passed as the argument.
Valid commands include \EXIT, \HELP, \TRACE, and \WHO. These
commands will be keyed by the user, or invoked as a result of a
special keystroke such as ESC, F1, etc.
The \EXIT command sets All_Done to non-zero (true), which will
eventually cause CHATTER to terminate.
The \HELP command causes a list of valid commands to be sent to the
display area.
The \TRACE command toggles the Trace identifier between zero and
nonzero states, with zero indicating Trace-is-off.
The \PROBE command broadcasts a PROBE message. The message is
transparent to the user, but causes all receiving stations to
transmit a RESPOND message to the originating station.
**********************************************************************/
void Execute_Command (char *CString)
{
char temp[32];
unsigned int sub;
memcpy (temp, CString, 9);
temp[9] = 0x00;
sub = 0;
while (sub < strlen (CString))
{
if (temp[sub] == 32)
{
temp[sub] = 0x00;
sub = 9;
}
if (temp[sub] >= 'a')
temp[sub] -= 32;
sub++;
}
if (strcmp (temp, "\\EXIT") == 0)
{
All_Done = 1;
return;
}
if (strcmp (temp, "\\HELP") == 0)
{
Screen_Write_Attr ("List of commands:", TEXT_COLOR);
Screen_Write_Attr (" \\EXIT or ESC: Terminate CHATTER",
TEXT_COLOR);
Screen_Write_Attr (" \\HELP or F1: This Display", TEXT_COLOR);
Screen_Write_Attr (" \\TRACE or F2: Toggles Trace Mode",
TEXT_COLOR);
Screen_Write_Attr (" \\WHO or F3: List of Conferencees",
TEXT_COLOR);
return;
}
if (strcmp (temp, "\\TRACE") == 0)
{
if (Trace)
{
Trace = 0;
Screen_Write_Attr ("TRACE is now off", TEXT_COLOR);
}
else
{
Trace = 1;
Screen_Write_Attr ("TRACE is now on", TEXT_COLOR);
}
return;
}
if (strcmp (temp, "\\WHO") == 0)
{
Setup_Send (PROBE_CODE, "");
return;
}
if (strcmp (temp, "\\NETS") == 0)
{
Screen_Write_Attr ("List of Active Networks:", TEXT_COLOR);
for (sub = 0; sub < NetNumCount; sub++)
{
sprintf (temp, " %.8lXh", NetworkNumber[sub]);
Screen_Write_Attr (temp, TEXT_COLOR);
}
return;
}
return;
}
/******************************************************************
This function checks all of the listen buffers to see if any of
them have a message. If so, the message is processed, and the
buffer is returned to the listen chain via the IPXLIB call
IPX_Listen_For_Packet. As soon as we find a message that has
been received, assuming trace is set, we compose and display
a message indicating the type of message, and the source.
Most message result in some kind of message being sent to the
display area, except for the PROBE message. If we receive a
PROBE message, we compose a RESPOND message and send it back to
whichever station sent the original PROBE message.
Also note that, for all messages, we take the source network
number and add it to our network number table if it isn't
already there.
******************************************************************/
void Poll_Net (void)
{
int dsize;
char msg[9], tmsg[80], cmsg[20], nmsg[30];
int sub, sub2;
unsigned long normnet;
for (sub = 0; sub < Buffers; sub++)
{
if ((ECBPointer[sub].In_Use == 0) &&
(ECBPointer[sub].Completion_Code == 0))
{
if (Trace)
{
switch (IPXHPointer[sub].Data[0])
{
case JOIN_CODE:
strcpy (cmsg, "JOIN ");
break;
case LEAVE_CODE:
strcpy (cmsg, "LEAVE ");
break;
case PROBE_CODE:
strcpy (cmsg, "PROBE ");
break;
case RESPOND_CODE:
strcpy (cmsg, "RESPOND");
break;
case MESSAGE_CODE:
strcpy (cmsg, "MESSAGE");
break;
default:
sprintf (cmsg, "UNKNOWN");
break;
}
Show_Node_Address (IPXHPointer[sub].Source.Node.ch, nmsg);
sprintf (tmsg, " - Reading %s packet from Network %u Node ",
cmsg, IPXHPointer[sub].Source.Network);
strcat (tmsg, nmsg);
Screen_Write_Attr (tmsg, TRACE_COLOR);
}
dsize = IPX_Flipword (IPXHPointer[sub].Length) - 30;
IPXHPointer[sub].Data[dsize] = 0x00;
switch (IPXHPointer[sub].Data[0])
{
case JOIN_CODE:
memcpy (msg, &IPXHPointer[sub].Data[1], 8);
msg[8] = 0x00;
Screen_Write_Attr (msg, IDENT_COLOR);
Screen_Append (" ");
Screen_Append_Attr ("<< Joining Conference >>",
JOIN_COLOR);
break;
case LEAVE_CODE:
memcpy (msg, &IPXHPointer[sub].Data[1], 8);
msg[8] = 0x00;
Screen_Write_Attr (msg, IDENT_COLOR);
Screen_Append (" ");
Screen_Append_Attr ("<< Leaving Conference >>",
JOIN_COLOR);
break;
case PROBE_CODE:
Setup_Send_Specific (RESPOND_CODE, "",
&IPXHPointer[sub].Source);
break;
case RESPOND_CODE:
memcpy (msg, &IPXHPointer[sub].Data[1], 8);
msg[8] = 0x00;
Screen_Write_Attr (msg, IDENT_COLOR);
Screen_Append (" ");
Screen_Append_Attr ("<< Responding to probe >>",
JOIN_COLOR);
break;
case MESSAGE_CODE:
memcpy (msg, &IPXHPointer[sub].Data[1], 8);
msg[8] = 0x00;
Screen_Write_Attr (msg, IDENT_COLOR);
Screen_Append (" ");
if (strlen (&IPXHPointer[sub].Data[9]) > 70)
IPXHPointer[sub].Data[79] = 0x00;
Screen_Append_Attr (&IPXHPointer[sub].Data[9],
MESSAGE_COLOR);
break;
}
normnet = IPXHPointer[sub].Source.Network;
IPX_Fliplong (&normnet);
for (sub2 = 0; sub2 < NetNumCount; sub2++)
if (normnet == NetworkNumber[sub2])
break;
if (sub2 == NetNumCount)
{
if (NetNumCount == NetNumCountMax)
{
Screen_Write_Attr ("Network Number Table Overflow",
TEXT_COLOR);
}
else
{
NetworkNumber[NetNumCount] = normnet;
NetNumCount++;
}
}
IPX_Listen_For_Packet (&ECBPointer[sub]);
}
}
return;
}
/**********************************************************************
This function uses DOS interrupt 21h to read keystrokes. If there
are no keystrokes waiting for us, we just return. Otherwise, we
look at what we got, and take action accordingly.
If we get a backspace (hex 08), we erase the most recent input
character from the input area, and back up the Bufsub counter.
If we get a carraige return (hex 0D), we set up the entire input
buffer as a network message and broadcast it, unless the first
input character is a backslash, in which case we try to interpret
the input buffer as a valid command (via Execute_Command).
If we get a two-byte sequence, we evaluate the second byte. If we
have an F1, F2, F3, or F4, we call Execute_Command. Otherwise, we
ignore the keystroke.
If we have an ESC key (hex 1B) we call Execute_Command.
If we have a control character, we ignore it.
Anything that gets through the previous checks is considered valid,
and is copied to the input buffer.
**********************************************************************/
void Poll_Key (void)
{
union REGS regs;
int scsub;
regs.h.ah = 0x0B; /* look for buffered characters */
int86 (0x21, ®s, ®s);
if (regs.h.al == 0x00) return; /* return if there aren't any */
regs.h.ah = 0x08; /* otherwise, go get the next one */
int86 (0x21, ®s, ®s);
if (regs.h.al == 0x08)
{
if (Bufsub == 0) return;
scsub = (Screen_Rows - 1) * 80 + Bufsub;
Screen[scsub].Character = 0x20;
Screen[scsub].Attribute = INPUT_COLOR;
Screen[scsub - 1].Character = '_';
Screen[scsub - 1].Attribute = INPUT_COLOR;
Bufsub--;
return;
}
if (regs.h.al == 0x0D)
{
if (Buffer[0] == '\\')
{
Execute_Command (Buffer);
Screen_ClearLast ();
Clear_Buffer ();
scsub = (Screen_Rows - 1) * 80;
Screen[scsub].Character = '_';
Screen[scsub].Attribute = INPUT_COLOR;
return;
}
Buffer [Bufsub] = 0x00;
Setup_Send (MESSAGE_CODE, Buffer);
Screen_ClearLast ();
Clear_Buffer ();
scsub = (Screen_Rows - 1) * 80;
Screen[scsub].Character = '_';
Screen[scsub].Attribute = INPUT_COLOR;
return;
}
if (regs.h.al == 0x00)
{
regs.h.ah = 0x08;
int86 (0x21, ®s, ®s);
switch (regs.h.al)
{
case 59:
Execute_Command ("\\HELP");
return;
case 60:
Execute_Command ("\\TRACE");
return;
case 61:
Execute_Command ("\\WHO");
return;
case 62:
Execute_Command ("\\NETS");
return;
}
return;
}
if (regs.h.al == 0x1B)
{
Execute_Command ("\\EXIT");
return;
}
if (regs.h.al < 0x20) return;
Buffer [Bufsub] = regs.h.al;
scsub = (Screen_Rows - 1) * 80 + Bufsub;
Screen[scsub].Character = regs.h.al;
Screen[scsub].Attribute = INPUT_COLOR;
if (Bufsub < 78)
{
Bufsub++;
}
Screen[scsub + 1].Character = '_';
Screen[scsub + 1].Attribute = INPUT_COLOR;
return;
}
/**********************************************************************
This routine is a simple way to control the invocation of the other
various poll routines. It really does nothing, in and of itself,
other than calling the Relinquish IPX function, so that IPX can get
some CPU time.
**********************************************************************/
void poll (void)
{
union REGS regs;
Poll_Key ();
Poll_Net ();
IPX_Relinquish_Control ();
return;
}
/**********************************************************************
This is where execution starts. We print the processor name, then
call the Initialization function. If the function returns zero
(false) indicating an error condition, we just quit. Otherwise,
we clear the All_Done flag, then call the poll routine until the
All_Done flag is non-zero. At that point, we call the Termination
function, then we return to DOS. (Actually, to the C startup code,
but that is a different story).
**********************************************************************/
int main (int argc, char *argv[])
{
printf ("%s\n", PROCESSOR_NAME);
if (Initialization (argc, argv) == 0x00) return (0x21);
All_Done = 0;
while (!(All_Done))
poll ();
Termination ();
return (0x00);
}